package cn.springcloud.alibaba.gateway.filter;

import cn.springcloud.alibaba.core.base.Result;
import cn.springcloud.alibaba.core.base.ResultCode;
import cn.springcloud.alibaba.gateway.config.SysParameterConfig;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.List;

@Slf4j
@Component
public class AccessRequestFilter implements GlobalFilter, Ordered {

	@Autowired
	private SysParameterConfig sysConfig; // 白名单url可以配置到数据库中，或者在 nacos 动态调整
	@Autowired
	private StringRedisTemplate stringRedisTemplate;

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		// 白名单放行，比如授权服务、静态资源.....
		String requestUrl = exchange.getRequest().getPath().value();
		if (checkUrls(sysConfig.getIgnoreUrls(), requestUrl)) {
			return chain.filter(exchange);
		}

		// 非白名单，检查token是否存在
		String token = getToken(exchange);
		if (StringUtils.isBlank(token)) {
			return buildReturnMono(Result.error(ResultCode.RESULT_LOGIN_EXPIRED), exchange);
		}

		ServerHttpRequest request = exchange.getRequest();
		String method = request.getMethodValue();
		InetSocketAddress remoteAddress = request.getRemoteAddress();
		String path = request.getPath().pathWithinApplication().value();

		return chain.filter(exchange)
			//filter的后置处理
			.then(Mono.fromRunnable(() -> {
				ServerHttpResponse response = exchange.getResponse();
				HttpStatus statusCode = response.getStatusCode();
				log.info("[{}]:{},[ip]:{},[status]:{}", method, path, remoteAddress, statusCode);
			}));
	}

	private Mono<Void> buildReturnMono(Result<String> result, ServerWebExchange exchange) {
		ServerHttpResponse response = exchange.getResponse();
		byte[] bits = JSON.toJSONString(result).getBytes(StandardCharsets.UTF_8);
		DataBuffer buffer = response.bufferFactory().wrap(bits);
		response.setStatusCode(HttpStatus.OK);
		response.getHeaders().add("Content-Type", "application/json;charset:utf-8");
		return response.writeWith(Mono.just(buffer));
	}

	/**
	 * 从请求头中获取Token
	 */
	private String getToken(ServerWebExchange exchange) {
		String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization");
		if (StringUtils.isBlank(tokenStr)) {
			return null;
		}
		String token = tokenStr.split(" ")[1];
		if (StringUtils.isBlank(token)) {
			return null;
		}
		return token;
	}

	/**
	 * 对url进行校验匹配
	 */
	private boolean checkUrls(List<String> urls, String path) {
		AntPathMatcher pathMatcher = new AntPathMatcher();
		for (String url : urls) {
			if (pathMatcher.match(url, path)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 值越小优先级越高
	 */
	@Override
	public int getOrder() {
		return Integer.MIN_VALUE;
	}
}
